package udpserver

import (
	"context"
	"encoding/binary"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"github.com/spf13/viper"
	"log"
	"net"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"tianlian-ttc/internal/pkg/packet"
	"time"

	"github.com/google/uuid"
	"github.com/streadway/amqp"
)

const (
	TelemetryEventExchange = "telemetry_exchange"
	TelemetryEventQueue    = "telemetry_event_queue"
	TelemetryEventBindKey  = "event.telemetry.#"
)

// PacketConfig 存储数据包配置
type PacketConfig struct {
	SourceID      uint32
	DestinationID uint32
}

// UDPServer represents a UDP server
// UDPServer 表示一个UDP服务器
type UDPServer struct {
	conn       *net.UDPConn
	remoteAddr *net.UDPAddr
	mu         sync.Mutex
	ctx        context.Context
	cancel     context.CancelFunc
	config     PacketConfig
}

var udpServerInstance *UDPServer
var udpServerInstance1 *UDPServer

// TelemetryState tracks telemetry saving state
type TelemetryState struct {
	LastSaveTime time.Time
	CurrentFile  string
}

// Message represents the RabbitMQ message structure
type Message struct {
	UUID         string `json:"uuid,omitempty"`
	Name         string `json:"name,omitempty"`
	RoutingKey   string `json:"routingKey,omitempty"`
	Priority     uint8  `json:"priority,omitempty"`
	Immutable    bool   `json:"immutable,omitempty"`
	RetryCount   int    `json:"retryCount,omitempty"`
	RetryTimeout int    `json:"retryTimeout,omitempty"`
	Source       string `json:"source,omitempty"`
	Body         string `json:"body,omitempty"` // JSON Object
}

// TelemetryData represents the telemetry data structure for MQ message
type TelemetryData struct {
	TargetCode    string    `json:"targetCode"`
	SourceID      string    `json:"sourceId"`
	FrequencyBand string    `json:"frequencyBand"`
	Timestamp     uint32    `json:"timestamp"`
	ReceivedAt    time.Time `json:"receivedAt"`
	Data          string    `json:"data"` // hex encoded frame data
}

// MQClient represents a RabbitMQ client
type MQClient struct {
	conn    *amqp.Connection
	channel *amqp.Channel
	config  struct {
		exchange     string
		exchangeType string
		queueName    string
		bindingKey   string
	}
}

var (
	telemetryStates = make(map[string]*TelemetryState) // key: targetCode_sourceID
	telemetryMutex  sync.RWMutex
	mqClient        *MQClient
)

// SatelliteCodeMap maps target codes to satellite names
var SatelliteCodeMap = map[string]string{
	"1b6501": "xr7",
	"1c6501": "xr09",
	"2f6501": "SJY01",
}

// GetSatelliteCode converts target code to satellite code
func GetSatelliteCode(targetCode []byte) string {
	hexCode := strings.ToLower(hex.EncodeToString(targetCode))
	if satCode, ok := SatelliteCodeMap[hexCode]; ok {
		return satCode
	}
	return hexCode // return original hex code if no mapping found
}

// initMQ initializes the RabbitMQ connection
func initMQ() error {
	// Create new MQ client
	mqClient = &MQClient{}
	mqClient.config.exchange = TelemetryEventExchange
	mqClient.config.exchangeType = "topic"
	mqClient.config.queueName = TelemetryEventQueue
	mqClient.config.bindingKey = TelemetryEventBindKey

	// Connect to RabbitMQ
	conn, err := amqp.Dial("amqp://starwiz:starwiz123@10.167.46.241:5672/")
	if err != nil {
		return fmt.Errorf("failed to connect to RabbitMQ: %v", err)
	}
	mqClient.conn = conn

	// Create channel
	ch, err := conn.Channel()
	if err != nil {
		conn.Close()
		return fmt.Errorf("failed to open channel: %v", err)
	}
	mqClient.channel = ch

	// Declare exchange
	if err := ch.ExchangeDeclare(
		mqClient.config.exchange,     // name
		mqClient.config.exchangeType, // type
		true,                         // durable
		false,                        // auto-deleted
		false,                        // internal
		false,                        // no-wait
		nil,                          // arguments
	); err != nil {
		ch.Close()
		conn.Close()
		return fmt.Errorf("failed to declare exchange: %v", err)
	}

	// Declare queue
	queue, err := ch.QueueDeclare(
		mqClient.config.queueName, // name
		true,                      // durable
		false,                     // delete when unused
		false,                     // exclusive
		false,                     // no-wait
		nil,                       // arguments
	)
	if err != nil {
		ch.Close()
		conn.Close()
		return fmt.Errorf("failed to declare queue: %v", err)
	}

	// Bind queue
	if err := ch.QueueBind(
		queue.Name,                 // queue name
		mqClient.config.bindingKey, // binding key
		mqClient.config.exchange,   // exchange
		false,
		nil,
	); err != nil {
		ch.Close()
		conn.Close()
		return fmt.Errorf("failed to bind queue: %v", err)
	}

	return nil
}

// Close closes the RabbitMQ connection
func (mq *MQClient) Close() {
	if mq.channel != nil {
		mq.channel.Close()
	}
	if mq.conn != nil {
		mq.conn.Close()
	}
}

// Publish publishes a message to RabbitMQ
func (mq *MQClient) Publish(msg *Message) error {
	data, err := json.Marshal(msg)
	if err != nil {
		return err
	}

	if mq.channel == nil {
		return fmt.Errorf("channel not initialized")
	}

	return mq.channel.Publish(

		mq.config.exchange, // exchange
		msg.RoutingKey,     // routing key
		false,              // mandatory
		false,              // immediate
		amqp.Publishing{
			MessageId:    msg.UUID,
			ContentType:  "application/json",
			Body:         data,
			Priority:     msg.Priority,
			DeliveryMode: amqp.Persistent,
		})
}

// publishTelemetryData publishes telemetry data to RabbitMQ
func publishTelemetryData(frameData []byte, header *packet.Header, status byte, timestamp uint32) error {
	if mqClient == nil {
		return fmt.Errorf("MQ client not initialized")
	}

	// Convert binary data to hex string
	hexData := hex.EncodeToString(frameData)

	// Get satellite code from target code
	//targetCodeHex := hex.EncodeToString(header.TargetCode)
	satelliteCode := GetSatelliteCode(header.TargetCode)

	// Determine frequency band
	var freqBand string
	switch status {
	case packet.TelemetryStatusX:
		freqBand = "X"
	case packet.TelemetryStatusUV:
		freqBand = "UV"
	case packet.TelemetryStatusS:
		freqBand = "S"
	default:
		freqBand = fmt.Sprintf("Unknown_%02x", status)
	}

	// Create telemetry data structure
	telemetryData := TelemetryData{
		TargetCode:    satelliteCode,
		SourceID:      fmt.Sprintf("%x", header.SourceID),
		FrequencyBand: freqBand,
		Timestamp:     timestamp,
		ReceivedAt:    time.Now(),
		Data:          hexData,
	}

	// Convert to JSON
	_, err := json.Marshal(telemetryData)
	if err != nil {
		return fmt.Errorf("failed to marshal telemetry data: %v", err)
	}

	msg := &Message{
		UUID:       uuid.New().String(),
		Priority:   0,
		Name:       "telemetry-data",
		Body:       hexData,
		RoutingKey: "event.telemetry." + satelliteCode,
		Source:     "udp-server",
	}

	if err := mqClient.Publish(msg); err != nil {
		log.Printf("Failed to publish message: %v, attempting to reconnect...", err)
		// Try to reconnect
		if initErr := initMQ(); initErr != nil {
			log.Printf("Failed to reinitialize RabbitMQ: %v", initErr)
			return fmt.Errorf("failed to publish message and reconnect: %v", err)
		}

		// Retry publish after reconnection
		if retryErr := mqClient.Publish(msg); retryErr != nil {
			log.Printf("Failed to publish message after reconnection: %v", retryErr)
			return fmt.Errorf("failed to publish message after reconnection: %v", retryErr)
		}
	}

	return nil
}

// StartServer starts the UDP server
// StartServer 启动UDP服务器
func StartServer() {
	// Initialize viper configuration
	viper.SetConfigName("config")
	viper.SetConfigType("yaml")
	viper.AddConfigPath("configs/")

	if err := viper.ReadInConfig(); err != nil {
		log.Fatalf("Error reading config file: %v", err)
	}

	// Initialize RabbitMQ connection
	if err := initMQ(); err != nil {
		log.Printf("Failed to initialize RabbitMQ: %v", err)
		// Continue even if MQ fails, we'll still save to files
	}

	// 获取配置
	// Get configuration
	localAddr := viper.GetString("server.udp.local_address")
	remoteAddr := viper.GetString("server.udp.remote_address")
	heartbeatInterval := viper.GetDuration("server.udp.heartbeat_interval")

	// 获取数据包配置
	packetConfig := PacketConfig{
		SourceID:      uint32(viper.GetInt("server.udp.packet.source_id")),
		DestinationID: uint32(viper.GetInt("server.udp.packet.destination_id")),
	}

	// 解析地址
	// Parse addresses
	laddr, err := net.ResolveUDPAddr("udp", localAddr)
	if err != nil {
		log.Fatalf("Error resolving local UDP address: %v", err)
	}

	laddr2, err := net.ResolveUDPAddr("udp", ":18891")
	if err != nil {
		log.Fatalf("Error resolving local UDP address: %v", err)
	}

	raddr, err := net.ResolveUDPAddr("udp", remoteAddr)
	if err != nil {
		log.Fatalf("Error resolving remote UDP address: %v", err)
	}

	ctx, cancel := context.WithCancel(context.Background())

	// 创建UDP连接
	// Create UDP connection
	conn, err := net.ListenUDP("udp", laddr)
	if err != nil {
		log.Fatalf("Error creating UDP connection: %v", err)
	}
	conn2, err := net.ListenUDP("udp", laddr2)
	if err != nil {
		log.Fatalf("Error creating UDP connection: %v", err)
	}

	udpServerInstance = &UDPServer{
		conn:       conn,
		remoteAddr: raddr,
		ctx:        ctx,
		cancel:     cancel,
		config:     packetConfig,
	}
	udpServerInstance1 = &UDPServer{
		conn:       conn2,
		remoteAddr: raddr,
		ctx:        ctx,
		cancel:     cancel,
		config:     packetConfig,
	}

	log.Printf("UDP server started on %s, remote address: %s", conn.LocalAddr(), raddr)

	// 启动接收协程
	// Start receive goroutine
	go udpServerInstance.receiveLoop()

	// 启动心跳
	// Start heartbeat
	go udpServerInstance1.startHeartbeat(heartbeatInterval)
}

// receiveLoop continuously receives UDP packets
// receiveLoop 持续接收UDP数据包
func (us *UDPServer) receiveLoop() {
	buffer := make([]byte, 1062)
	for {
		select {
		case <-us.ctx.Done():
			return
		default:
			n, addr, err := us.conn.ReadFromUDP(buffer)
			if err != nil {
				log.Printf("Error reading UDP packet: %v", err)
				continue
			}

			// Log raw data
			log.Printf("Received raw data from %s: %s", addr, hex.EncodeToString(buffer))

			// Parse header
			header, err := packet.Parse(buffer)
			if err != nil {
				log.Printf("Error parsing packet header: %v", err)
				continue
			}

			// Log header information
			log.Printf("Packet Header: TargetCode=%x, SourceID=0x%x, DestinationID=0x%x, DataFlag=0x%x, SequenceNumber=%d",
				header.TargetCode, header.SourceID, header.DestinationID, header.DataFlag, header.SequenceNumber)

			// Handle different message types based on DataFlag
			switch header.DataFlag {
			case packet.FlagLinkMonitor:
				log.Printf("Received Link Monitor packet")
				// Process link monitor packet
				linkMonitorPacket, err := packet.ParseLinkMonitorPacket(buffer[:n])
				if err != nil {
					log.Printf("Error parsing link monitor packet: %v", err)
					continue
				}
				log.Printf("Link Monitor Data: %+v", linkMonitorPacket)

			case packet.FlagReceiveResponse:
				log.Printf("Received Response packet")
				// Process response packet
				// TODO: Implement response packet handling

			case packet.FlagTelemetryData:
				log.Printf("Received Telemetry Data packet")
				// Save telemetry data
				if err := saveTelemetryData(buffer[:n]); err != nil {
					log.Printf("Error saving telemetry data: %v", err)
				}

			case packet.FlagControlData:
				log.Printf("Received Control Data packet")
				// Process control data packet
				// TODO: Implement control data packet handling

			default:
				log.Printf("Unknown packet type: 0x%x", header.DataFlag)
			}

			us.mu.Lock()
			us.remoteAddr = addr
			us.mu.Unlock()
		}
	}
}

// startHeartbeat starts sending periodic heartbeat messages
func (us *UDPServer) startHeartbeat(interval time.Duration) {
	ticker := time.NewTicker(interval)
	defer ticker.Stop()

	for {
		select {
		case <-us.ctx.Done():
			return
		case <-ticker.C:
			packet := us.createLinkMonitorPacket()
			if err := us.SendLinkMonitorPacket(packet); err != nil {
				log.Printf("Error sending heartbeat: %v", err)
			}
		}
	}
}

// createLinkMonitorPacket creates a link monitor packet
func (us *UDPServer) createLinkMonitorPacket() []byte {
	// Create a new link monitor packet using our packet package
	linkMonitorPacket := packet.NewLinkMonitorPacket(
		us.config.SourceID,
		us.config.DestinationID,
	)

	// Convert the packet to bytes
	return linkMonitorPacket.Bytes()
}

// saveTelemetryData saves telemetry data to a file based on TargetCode and time
// saveTelemetryData 根据目标代号和时间将遥测数据保存到文件
func saveTelemetryData(data []byte) error {
	// Parse the packet header
	header, err := packet.Parse(data)
	if err != nil {
		return fmt.Errorf("failed to parse packet header: %v", err)
	}

	// Get telemetry frame data (skip header)
	telemetryData := data[packet.HeaderLength:]
	if len(telemetryData) < 5 { // Minimum 1 byte status + 4 bytes timestamp
		return fmt.Errorf("telemetry data too short: %d bytes", len(telemetryData))
	}

	// Extract status and timestamp
	status := telemetryData[0]
	timestamp := binary.LittleEndian.Uint32(telemetryData[1:5])

	// Get actual telemetry frame (skip status and timestamp)
	frameData := telemetryData[5:]

	// Get satellite code from target code
	satelliteCode := GetSatelliteCode(header.TargetCode)
	sourceIDHex := fmt.Sprintf("%x", header.SourceID)

	// Add frequency band info to directory structure
	var freqBand string
	switch status {
	case packet.TelemetryStatusX:
		freqBand = "X"
	case packet.TelemetryStatusUV:
		freqBand = "UV"
	case packet.TelemetryStatusS:
		freqBand = "S"
	default:
		freqBand = fmt.Sprintf("Unknown_%02x", status)
	}

	stateKey := fmt.Sprintf("%s_%s_%s", satelliteCode, sourceIDHex, freqBand)

	telemetryMutex.Lock()
	defer telemetryMutex.Unlock()

	now := time.Now()
	state, exists := telemetryStates[stateKey]

	// Check if we need to create a new file
	needNewFile := !exists || // First time for this target
		state.CurrentFile == "" || // No current file
		now.Sub(state.LastSaveTime) > 10*time.Minute // More than 10 minutes passed

	if needNewFile {
		// Create directory based on satellite code and frequency band
		baseDir := filepath.Join("data", "telemetry", satelliteCode, freqBand)
		if err := os.MkdirAll(baseDir, 0755); err != nil {
			return fmt.Errorf("failed to create directory %s: %v", baseDir, err)
		}

		// Create new filename with format: SatelliteCode_SourceID_FreqBand_timestamp
		timestamp := now.Format("20060102_150405")
		filename := filepath.Join(baseDir, fmt.Sprintf("%s_%s_%s_%s.dat",
			satelliteCode, sourceIDHex, freqBand, timestamp))

		// Update or create state
		telemetryStates[stateKey] = &TelemetryState{
			LastSaveTime: now,
			CurrentFile:  filename,
		}
		state = telemetryStates[stateKey]

		log.Printf("Creating new telemetry file: %s", filename)
	}

	// Open file in append mode
	file, err := os.OpenFile(state.CurrentFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		return fmt.Errorf("failed to open file %s: %v", state.CurrentFile, err)
	}
	defer file.Close()

	// Write only the telemetry frame data
	if _, err := file.Write(frameData); err != nil {
		return fmt.Errorf("failed to write to file %s: %v", state.CurrentFile, err)
	}

	// Update last save time
	state.LastSaveTime = now

	// Publish to RabbitMQ
	if err := publishTelemetryData(frameData, header, status, timestamp); err != nil {
		log.Printf("Failed to publish telemetry data to MQ: %v", err)
		// Continue even if MQ publish fails
	} else {
		log.Printf("Published telemetry data to MQ for target %s", satelliteCode)
	}

	log.Printf(" bytes of telemetry frame to %s ",
		hex.EncodeToString(frameData))

	log.Printf("Saved %d bytes of telemetry frame to %s (Status: %s, Timestamp: %d)",
		len(frameData), state.CurrentFile, freqBand, timestamp)
	return nil
}

// SendCommand sends a command to the UDP server
// SendCommand 发送命令到UDP服务器
func SendCommand(command []byte) error {
	if udpServerInstance1 == nil {
		return fmt.Errorf("UDP server not initialized")
	}

	udpServerInstance1.mu.Lock()
	raddr := udpServerInstance1.remoteAddr
	udpServerInstance1.mu.Unlock()

	if raddr == nil {
		return fmt.Errorf("no remote address available")
	}

	_, err := udpServerInstance.conn.WriteToUDP(command, raddr)
	if err != nil {
		return fmt.Errorf("error sending command: %v", err)
	}

	return nil
}

// SendLinkMonitorPacket sends a link monitor packet
// SendLinkMonitorPacket 发送链路监视数据包
func (us *UDPServer) SendLinkMonitorPacket(packet []byte) error {
	us.mu.Lock()
	raddr := us.remoteAddr
	us.mu.Unlock()

	if raddr == nil {
		return fmt.Errorf("no remote address available")
	}
	log.Println("使用现有的UDP连接心跳指令" + hex.EncodeToString(packet))
	_, err := us.conn.WriteToUDP(packet, raddr)
	if err != nil {
		return fmt.Errorf("error sending link monitor packet: %v", err)
	}

	return nil
}

// Stop stops the UDP server
func Stop() {
	if udpServerInstance != nil {
		udpServerInstance.cancel()
		if udpServerInstance.conn != nil {
			udpServerInstance.conn.Close()
		}
	}
}
